﻿using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Extensions.Services.Authentication.Policys;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;

namespace Extensions.Services.Authentication;

public static class AddAuthentication
{
    public static IServiceCollection AddAuthenticationSetup(this IServiceCollection services)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));

        var enable = Appsettings.app("Authentication", "Enable").ToBool();
        if (!enable)
        {
            return services;
        }
        var iss = Appsettings.app("Authentication", "Issuer");
        var aud = Appsettings.app("Authentication", "Audience");
        var secretKey = Appsettings.app("Authentication", "Secret");
        //var exp = Appsettings.app("Authentication", "Exp").ToInt32();//过期时间

        var keyByteArray = Encoding.ASCII.GetBytes(secretKey);
        var signingKey = new SymmetricSecurityKey(keyByteArray);
        //var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
        var providerKey = Appsettings.app("Authentication", "AuthenticationProviderKey");
        // 这个参数必须设置 要配合ocelot来使用
        ArgumentException.ThrowIfNullOrEmpty(providerKey);

        var tokenValidationParameters = new TokenValidationParameters()
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,
            ValidateIssuer = true,
            ValidIssuer = iss,
            ValidateAudience = true,
            ValidAudience = aud,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromSeconds(30),// 超时30s内依然可以验证通过
            RequireExpirationTime = true
        };

        services.AddAuthentication(options =>
        {
            // 采用jwtbearer认证
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            // 添加验证失败时的处理
            options.DefaultChallengeScheme = nameof(TokenValidateHandler);
            options.DefaultForbidScheme = nameof(TokenValidateHandler);
        })//添加jwtbearer服务
            .AddJwtBearer(providerKey, options =>
        {
            options.TokenValidationParameters = tokenValidationParameters;
            // 对于一些验证失败的处理  可以再失败后  加一些header表示  是因为什么失败的
            options.Events = new JwtBearerEvents()
            {
                OnChallenge = context =>
                {
                    context.Response.Headers.Add("Token-Error", context.ErrorDescription);
                    return Task.CompletedTask;
                },
                OnAuthenticationFailed = context =>
                {
                    var jwtHandler = new JwtSecurityTokenHandler();
                    var token = context.Request.Headers["Authorization"].ToString().Trim().Replace("Bearer ", "");

                    if (!string.IsNullOrWhiteSpace(token) && jwtHandler.CanReadToken(token))
                    {
                        var jwtToken = jwtHandler.ReadJwtToken(token);

                        if (jwtToken.Issuer != iss)
                        {
                            context.Response.Headers.Add("Token-Error-Iss", "issuer is wrong!");
                        }

                        if (jwtToken.Audiences.FirstOrDefault() != aud)
                        {
                            context.Response.Headers.Add("Token-Error-Aud", "Audience is wrong!");
                        }
                    }

                    // 如果过期，则把<是否过期>添加到，返回头信息中
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("Token-Expired", "true");
                    }
                    return Task.CompletedTask;
                }
            };
        }).AddScheme<AuthenticationSchemeOptions, TokenValidateHandler>(nameof(TokenValidateHandler), o => { }); ;
        // 重写了 验证失败时的返回


        return services;
    }
}